/*
 * Copyright (C) 2006 Johan Maasing johan at zoom.nu Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package nu.zoom.catonine.stylerule;

import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.UUID;
import java.util.regex.Pattern;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import nu.zoom.catonine.xml.JAXBColorAdapter;
import nu.zoom.catonine.xml.JAXBFontAdapter;
import nu.zoom.catonine.xml.JAXBPatternAdapter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Style information for lines matching a pattern. This class is not thread
 * safe.
 * 
 * @author "Johan Maasing" &lt;johan@zoom.nu&gt;
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "guid", "supress", "fold", "foreground", "background", "pattern", "font" })
public class StyleRule implements Serializable, ChangeTracker {

	private static final long serialVersionUID = 1L;
	private transient Log log = LogFactory.getLog(getClass());
	private transient boolean changed = false;
	private transient boolean disabled;
	private transient ArrayList<StyleRuleListener> listeners = new ArrayList<StyleRuleListener>();

	private String guid;
	private boolean supress;
	private boolean fold;
	@XmlJavaTypeAdapter(JAXBColorAdapter.class)
	private Color foreground;
	@XmlJavaTypeAdapter(JAXBColorAdapter.class)
	private Color background;
	@XmlJavaTypeAdapter(JAXBPatternAdapter.class)
	private Pattern pattern;
	@XmlJavaTypeAdapter(JAXBFontAdapter.class)
	private Font font;

	public StyleRule() {
		super();
		guid = UUID.randomUUID().toString();
	}

	public synchronized Font getFont() {
		return font;
	}

	public synchronized Color getBackground() {
		return background;
	}

	public synchronized Color getForeground() {
		return foreground;
	}

	public synchronized Pattern getPattern() {
		return pattern;
	}

	public synchronized String getGuid() {
		return guid;
	}

	public synchronized boolean isSupress() {
		return supress;
	}

	public synchronized void setFont(Font font) {
		this.font = font;
		firePatternChanged();
	}

	public synchronized void setBackground(Color background) {
		this.background = background;
		firePatternChanged();
	}

	public synchronized void setDisabled(boolean disabled) {
		this.disabled = disabled;
		firePatternChanged();
	}

	public synchronized void setForeground(Color foreground) {
		this.foreground = foreground;
		firePatternChanged();
	}

	public synchronized void setPattern(final String pattern) {
		this.pattern = Pattern.compile(pattern, Pattern.DOTALL);
		firePatternChanged();
	}

	public synchronized void setSupress(boolean supress) {
		this.supress = supress;
		firePatternChanged();
	}

	public void setFold(boolean fold) {
		this.fold = fold;
		firePatternChanged();
	}

	public boolean isFold() {
		return fold;
	}

	public synchronized boolean isDisabled() {
		return disabled;
	}

	public void addListener(StyleRuleListener listener) {
		log.trace("Adding listener:" + listener);
		synchronized (getListeners()) {
			getListeners().add(listener);
		}
	}

	public void removeListener(StyleRuleListener listener) {
		log.trace("Remove listener:" + listener);
		synchronized (getListeners()) {
			getListeners().remove(listener);
		}
	}

	private void firePatternChanged() {
		log.trace("Notifying listeners that pattern has changed");
		changed = true;
		ArrayList<Thread> notifierThreads = new ArrayList<Thread>();
		synchronized (getListeners()) {
			for (final StyleRuleListener listener : getListeners()) {
				Thread t = new Thread(new Runnable() {

					@Override
					public void run() {
						listener.patternChanged(StyleRule.this);
					}
				});
				notifierThreads.add(t);
			}
		}
		for (Thread t : notifierThreads) {
			t.start();
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof StyleRule) {
			return guid.equals(((StyleRule) obj).guid);
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() {
		int hash = 5;
		hash = 97 * hash + (this.guid != null ? this.guid.hashCode() : 0);
		return hash;
	}

	@Override
	public String toString() {
		return (pattern == null) ? "<no pattern>" : pattern.toString();
	}

	@Override
	public boolean isChanged() {
		return changed;
	}

	@Override
	public void resetChangeStatus() {
		changed = false;
	}

	private ArrayList<StyleRuleListener> getListeners() {
		if (listeners == null) {
			listeners = new ArrayList<StyleRuleListener>();
		}
		return listeners;
	}
}
